package com.zb.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zb.constant.AccountConstant;
import com.zb.entity.AdmInfo;
import com.zb.entity.LoginToken;
import com.zb.entity.ZjAdmAccount;
import com.zb.entity.dto.*;
import com.zb.enums.DeviceType;
import com.zb.enums.LoginType;
import com.zb.exception.LoginException;
import com.zb.exception.RegisterException;
import com.zb.exception.SMSException;
import com.zb.mapper.ZjAdmAccountMapper;
import com.zb.redis.RedisLoginTokenUtil;
import com.zb.secure.BCryptUtil;
import com.zb.service.VerificSMSService;
import com.zb.service.ZjAdmAccountService;
import com.zb.utils.VerificUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Objects;

/**
 * <p>
 * 后台账号表 服务实现类
 * </p>
 *
 * @author WEN
 * @since 2023-07-07
 */
@Service
public class ZjAdmAccountServiceImpl extends ServiceImpl<ZjAdmAccountMapper, ZjAdmAccount> implements ZjAdmAccountService {
    @Autowired
    private ZjAdmAccountMapper admAccountMapper;

    // 注册账号
    @Override
    public void registerAccount(AccountDto accountDto) throws RegisterException {
        if (null != admAccountMapper.existByPhone(accountDto.getPhone())) {
            throw new RegisterException("该手机号已被注册");
        }
        ZjAdmAccount account = new ZjAdmAccount();
        account.setId(IdWorker.getId());
        account.setPhone(accountDto.getPhone());
        account.setSalt(BCryptUtil.generatorSalt());
        account.setAccount("zb_" + account.getSalt().substring(10, 20));
        account.setPassword(BCryptUtil.encryPassword(accountDto.getPassword(), account.getSalt()));
        account.setName(accountDto.getName());
        account.setNickName(accountDto.getNickName());
        account.setAvatar("");
        account.setStatus(0);
        account.setUpdateAccount(false);
        this.save(account);
    }

    // 登录
    @Override
    public LoginToken login(LoginDto loginDto) throws LoginException, SMSException {
        if (!Objects.equals(LoginType.ACCOUNT.getType(), loginDto.getType())
                && !Objects.equals(LoginType.VERIFIC_CODE.getType(), loginDto.getType())) {
            throw new IllegalArgumentException("传入的登录类型不合法！");
        }
        Assert.notEmpty(loginDto.getPhone(), "登录账号或手机号不能为空！");

        if (Objects.equals(LoginType.VERIFIC_CODE.getType(), loginDto.getType()) && !VerificUtil.phoneVerific(loginDto.getPhone())) {
            throw new IllegalArgumentException("请输入正确的手机号格式！");
        }
        // 校验验证码
        if (Objects.equals(LoginType.VERIFIC_CODE.getType(), loginDto.getType())) {
            VerificSMSService.checkLoginVerificCode(loginDto.getPhone(), loginDto.getPassword());
        }
        ZjAdmAccount account = admAccountMapper.getByphoneOrAccount(loginDto.getPhone());
        Assert.notNull(account, "登录账号或手机号不存在！", loginDto.getPhone());

        if (Objects.equals(LoginType.ACCOUNT.getType(), loginDto.getType())) {
            Assert.notEmpty(loginDto.getPassword(), "账号或密码错误！");
            String password = BCryptUtil.encryPassword(loginDto.getPassword(), account.getSalt());
            Assert.isTrue(Objects.equals(account.getPassword(), password), "账号或密码错误！");
        }
        if (Objects.equals(1, account.getStatus())) {
            throw new LoginException("您的账号已被冻结！");
        }
        if (Objects.equals(2, account.getStatus())) {
            throw new LoginException("您的账号已被封禁！");
        }
        if (StpUtil.isLogin(account.getId())) {
            String oldAccessToken = StpUtil.getTokenValueByLoginId(account.getId(), DeviceType.PC.getType());
            // 删除旧的登录缓存信息
            RedisLoginTokenUtil.removeAllTokenByAccessToken(oldAccessToken);
            RedisLoginTokenUtil.removeLoginAccessToken(oldAccessToken);
        }

        // Sa-Token登录
        if (loginDto.isRememberMe()) {
            StpUtil.login(account.getId(),
                    new SaLoginModel()
                            .setDevice(DeviceType.PC.getType()) // 登录端
                            .setIsLastingCookie(true) // 记住我
                            .setTimeout(AccountConstant.LOGIN_TOKEN_REMEMBERME_EXPIRE_TIME) // 七天有效期
            );
        } else {
            StpUtil.login(account.getId(), DeviceType.PC.getType());
        }

        // 获取登录信息
        SaTokenInfo saToken = StpUtil.getTokenInfo();

        // 封装登录信息返回给前端
        LoginToken loginToken = new LoginToken(
                saToken.getTokenName(),
                saToken.getTokenValue(),
                RedisLoginTokenUtil.generatorRefreshToken(),
                saToken.getTokenTimeout()
        );

        AdmInfo admInfo = new AdmInfo(account.getId(), account.getAccount(), account.getPhone(), account.getName(), loginDto.isRememberMe(), DeviceType.PC.getType());
        RedisLoginTokenUtil.setAdmInfoByAccessToken(saToken.getTokenValue(), admInfo);
        RedisLoginTokenUtil.setRefreshTokenByAccessToken(loginToken.getTokenValue(), loginToken.getRefreshToken(), admInfo.isRememberMe());
        RedisLoginTokenUtil.setAccessTokenByRefreshToken(loginToken.getRefreshToken(), loginToken.getTokenValue(), admInfo.isRememberMe());

        // 更新登录时间
        this.updateLastLoginTime(account.getId());
        return loginToken;
    }

    // 根据ID获取账号信息
    @Override
    public AccountLoginInfo getInfo(Long id) {
        ZjAdmAccount account = this.getById(id);
        return AccountLoginInfo.beanToInfo(account);
    }

    // 更改登录密码
    @Override
    public void updatePassword(PasswordDto passwordDto) throws LoginException, SMSException {
        Assert.isTrue(Objects.equals(1, passwordDto.getType()) || Objects.equals(2, passwordDto.getType()), "更改密码的类型不合法", passwordDto.getType());
        ZjAdmAccount account = null;
        // 验证码改密
        if (Objects.equals(2, passwordDto.getType())) {
            VerificSMSService.checkUpdateVerificCode(passwordDto.getPhone(), passwordDto.getOldPassword());
            account = admAccountMapper.getByPhone(passwordDto.getPhone());
            Assert.notNull(account, "手机号未注册，无法修改密码");
        } else {
            account = admAccountMapper.getByPhone(passwordDto.getPhone());
            Assert.notNull(account, "手机号未注册，无法修改密码");
            String oldPassword = BCryptUtil.encryPassword(passwordDto.getOldPassword(), account.getSalt());
            Assert.isTrue(Objects.equals(account.getPassword(), oldPassword), "旧密码输入错误!", null);
        }
        ZjAdmAccount updateAccount = new ZjAdmAccount();
        updateAccount.setId(account.getId());
        updateAccount.setSalt(BCryptUtil.generatorSalt());
        updateAccount.setPassword(BCryptUtil.encryPassword(passwordDto.getNewPassword(), updateAccount.getSalt()));
        this.updateById(updateAccount);
    }

    // 更改账号信息
    @Override
    public void updateAccountInfo(Long id, AccountInfoDto accountInfoDto) {
        ZjAdmAccount account = new ZjAdmAccount();
        account.setId(id);
        account.setName(accountInfoDto.getName());
        account.setNickName(accountInfoDto.getNickName());
        account.setAvatar(accountInfoDto.getAvatar());
        this.updateById(account);
    }

    // 更换手机号
    @Override
    public void updatePhone(AdmInfo loginInfo, String newPhone, String code) throws RegisterException, SMSException {
        // 验证码校验
        VerificSMSService.checkUpdateVerificCode(loginInfo.getPhone(), code);

        // 校验手机号
        Assert.isTrue(VerificUtil.phoneVerific(newPhone), "请输入正确的手机号格式！", newPhone);
        // 校验手机号是否已经被使用
        if (null != admAccountMapper.existByPhone(newPhone)) {
            throw new RegisterException("该手机号已被使用，不可更改！");
        }
        ZjAdmAccount account = new ZjAdmAccount();
        account.setId(loginInfo.getId());
        account.setPhone(newPhone);
        this.updateById(account);

        loginInfo.setPhone(newPhone);
    }

    // 更换登录账号
    @Override
    public void updateAccount(AdmInfo loginInfo, String account, String code) throws RegisterException, SMSException {
        // 验证码校验
        VerificSMSService.checkUpdateVerificCode(loginInfo.getPhone(), code);

        // 校验手机号是否已经被使用
        if (null != admAccountMapper.existByAccount(account)) {
            throw new RegisterException("该登录账号已被使用，不可更改！");
        }
        ZjAdmAccount admAccount = new ZjAdmAccount();
        admAccount.setId(loginInfo.getId());
        admAccount.setAccount(account);
        admAccount.setUpdateAccount(true);
        this.updateById(admAccount);

        loginInfo.setAccount(account);
    }

    // 更新最新登录时间
    @Override
    public void updateLastLoginTime(Long id) {
        ZjAdmAccount admAccount = new ZjAdmAccount();
        admAccount.setId(id);
        admAccount.setLastLoginTime(new Date());
        this.updateById(admAccount);
    }


}
